package com.scau402.system1.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scau402.common.annotation.Log;
import com.scau402.common.constant.DeptConstants;
import com.scau402.common.constant.InboundConstants;
import com.scau402.common.constant.JadeConstants;
import com.scau402.common.core.controller.BaseController;
import com.scau402.common.core.domain.AjaxResult;
import com.scau402.common.core.page.TableDataInfo;
import com.scau402.common.enums.BusinessType;
import com.scau402.common.enums.outbound.OutboundType;
import com.scau402.common.exception.ServiceException;
import com.scau402.common.utils.DataUtils;
import com.scau402.common.utils.SecurityUtils;
import com.scau402.common.utils.poi.ExcelUtil;
import com.scau402.system.domain.WmsInboundType;
import com.scau402.system.domain.WmsJade;
import com.scau402.system.service.IWmsJadeService;
import com.scau402.system1.domain.dto.WmsOutboundOrderDTO;
import com.scau402.system1.domain.factory.WmsOutboundOrderFactory;
import com.scau402.system1.domain.po.WmsInboundOrderDetailPO;
import com.scau402.system1.domain.po.WmsInboundOrderPO;
import com.scau402.system1.domain.po.WmsOutboundOrderDO;
import com.scau402.system1.domain.po.WmsOutboundOrderDetailDO;
import com.scau402.system1.domain.query.WmsOutboundOrderQuery;
import com.scau402.system1.domain.vo.WmsOutboundOrderAndDetailVO;
import com.scau402.system1.mapper.IWmsOutboundOrderMapper;
import com.scau402.system1.service.*;
import com.scau402.system1.service.impl.WmsWarehouseStorageService;
import com.scau402.system1.utils.IdGenerator;
import com.scau402.constants.RedisConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.scau402.constants.RedisConstants.*;

/**
 * <p>
 * 商品出库单主表 前端控制器
 * </p>
 *
 * @author
 * @since 2023-12-23
 */
@RestController
@RequestMapping("/system/outboundOrderV2")
@Slf4j
public class WmsOutboundOrderControllerV2 extends BaseController  {

    @Autowired
    private IWmsOutboundOrderService wmsOutboundOrderService;

    @Autowired
    private WmsWarehouseStorageService wmsWarehouseStorageService;

    @Autowired
    private IWmsJadeService iWmsJadeService;

    @Autowired
    private IWmsOutboundOrderDetailService outboundOrderDetailService;

    @Autowired
    private IWmsInboundOrderDetailService iWmsInboundOrderDetailService;

    @Autowired
    private IWmsInboundOrderService inboundOrderService;

    @Autowired
    private WmsOutboundOrderFactory outboundOrderFactory;


    @Autowired
    private IWmsAuditOrderService auditOrderService;

    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private IWmsOutboundOrderMapper outboundOrderMapper;

    /**
     * 查询商品出库单列表
     * @param query 查询参数实体类
     * @return 分页结果
     */
    @PreAuthorize("@ss.hasPermi('system:outboundOrder:list')")
    @GetMapping("/list")
    public TableDataInfo list(WmsOutboundOrderQuery query) {
        return wmsOutboundOrderService.selectWmsOutboundOrderListPage(query);
    }

    /**
     * 导出商品出库单列表
     * @param response 响应数据
     * @param query 查询参数实体类
     * @since 2024/2/17
     */
    @PreAuthorize("@ss.hasPermi('system:outboundOrder:export')")
    @Log(title = "商品出库单", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, @RequestBody WmsOutboundOrderQuery query) {
        List<WmsOutboundOrderAndDetailVO> wmsOutboundOrderAndDetailVOS = wmsOutboundOrderService.selectWmsOutboundOrderListByCodes(query);
        ExcelUtil<WmsOutboundOrderAndDetailVO> util = new ExcelUtil<WmsOutboundOrderAndDetailVO>(WmsOutboundOrderAndDetailVO.class);
        util.exportExcel(response, wmsOutboundOrderAndDetailVOS, "商品出库单数据", "出库单数据");
    }


    /**
     * 根据图片id集合或者条形码返回对应的翡翠信息
     */
    @GetMapping("/getJades")
    public AjaxResult getInfoByBarCode(@RequestParam(value = "imageIds", required = false) List<Long> imageIds,
                                       @RequestParam(value = "barcode", required = false) String barcode,
                                       @RequestParam(value = "status",required = false) String status) {
//        构建查询参数 查询指定仓库的在仓货品
        WmsJade wmsJade = new WmsJade();
        if (DataUtils.isNotEmpty(imageIds)){
            wmsJade.setImageIds(imageIds);
        }
        if (DataUtils.isNotEmpty(barcode)){
            wmsJade.setJadeBarcode(barcode);
        }
        if (DataUtils.isNotEmpty(status)){
            wmsJade.setStatus(status);
        }
        if (DataUtils.isAllEmpty(imageIds,barcode)){
            return error("请输入图片id或条形码");
        }
        List<WmsJade> wmsJadeList= iWmsJadeService.selectWmsJadeList(wmsJade);
        if(DataUtils.isEmpty(wmsJadeList)){
            return error("该翡翠条形码不存在或已出库");
        }
        return success(wmsJadeList);
    }

    /**
     * 新增商品出库单
     */
    @PreAuthorize("@ss.hasPermi('system:outboundOrder:add')")
    @Log(title = "商品出库单", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult add(@RequestBody WmsOutboundOrderDTO wmsOutboundOrderDTO) {
        Long tenantId = SecurityUtils.getLoginUser().getTenantId();

        // 防重入
        String preKey = OUTBOUND_PREFIX+tenantId+":"+wmsOutboundOrderDTO.getOrderCode();
        Long num = stringRedisTemplate.opsForValue().increment(preKey, 1L);
        log.info("出库操作：Redis计数器: {}",num);
        //已经有一个对应单号的入库单在创建
        if(num.equals(1L)){
            throw new ServiceException("出库单重复提交");
        }
        stringRedisTemplate.expire(preKey,5, TimeUnit.SECONDS);

        String dupPrefix = OUTBOUND_PREFIX + DUP_KEY + tenantId + ":" + wmsOutboundOrderDTO.getOrderCode();
        //查询缓存是否存在限制
        if (stringRedisTemplate.opsForValue().get(dupPrefix)!=null) {
            //存在则重新设置过期时间
            stringRedisTemplate.expire(dupPrefix,1+new Random().nextLong(), TimeUnit.SECONDS);
            throw new ServiceException("出库单重复提交");
        }
        //检查数据库中是否存在对应数据，存在则设置缓存
        if(outboundOrderMapper.selectOne(new QueryWrapper<WmsOutboundOrderDO>().eq("order_code",wmsOutboundOrderDTO.getOrderCode()).eq("tenant_id",tenantId))!=null){
            //执行lua脚本设置缓存
            stringRedisTemplate.execute(RedisConstants.DUPLICATE_SCRIPT,new ArrayList<>(), dupPrefix,"",1000L+new Random().nextInt(1000));
            throw new ServiceException("出库单重复提交");
        }

        // 设置租户ID
        WmsOutboundOrderDO wmsOutboundOrderDO =outboundOrderFactory.toWmsOutboundOrderDO(wmsOutboundOrderDTO);

//        //判断货品入库类型
//        Long inboundOrderId=wmsOutboundOrderDTO.getInboundOrderId();
//        String inboundType=wmsOutboundOrderService.getInboundTypeOrder(inboundOrderId);
//        String outboundType=wmsOutboundOrderDTO.getOrderType();
//        if (inboundType.equals(InboundConstants.INBOUND_ORDER_TYPE_BORROW)&&(!outboundType.equals(OutboundType.RETURN.toString()))){
//            return error("借货入库货品应进行市场还货");
//        }else if(inboundType.equals(InboundConstants.INBOUND_ORDER_TYPE_PROCESS)&&(!outboundType.equals(OutboundType.PROCESS.toString()))){
//            return error("加工入库货品应进行加工出库");
//        }


        // 判断如果为借货出库的，借货时间不能大于365天
        boolean isBorrowOutBound = false;
        if (wmsOutboundOrderDO.getOrderType().equals(OutboundType.BORROW.getCode())){
            isBorrowOutBound = true;
            if (wmsOutboundOrderDO.getLendDays()>365) {
                return error("借货天数不能大于365天");
            }
            else if(wmsOutboundOrderDO.getLendDays()<0){
                return error("借货天数不能小于0天");
            }
        }
        List<WmsJade> wmsJadeList = wmsOutboundOrderDTO.getWmsJadeList();
        //判断货品的状态是否为在仓，过滤出不在仓的翡翠
        List<WmsJade> filterList = wmsJadeList.stream()
                .filter(wmsJade -> !Objects.equals(wmsJade.getStatus(), JadeConstants.JADE_IN_WAREHOUSE))
                .collect(Collectors.toList());
        if (!filterList.isEmpty()){
            error("请选择在仓的货品!");
        }

        //判断是否为还货出库的
        boolean isReturnOutbound;
        Map<Long,Long> jadeIdMap;
        WmsInboundOrderPO byInboundOrderCode = null;
        //  如果为还货出库，那么需要把对应的入库单号拿到,并对明细后，把入库明细id弄出来
        if (Objects.equals(wmsOutboundOrderDO.getOrderType(), OutboundType.RETURN.getCode())){
            if (!inboundOrderService.checkExists(wmsOutboundOrderDO.getInboundOrderCode())){
                return error("不存在该借货入库单号！");
            }
            isReturnOutbound =true;
            String inboundOrderCode = wmsOutboundOrderDO.getInboundOrderCode();

            byInboundOrderCode = inboundOrderService.getByInboundOrderCode(inboundOrderCode);
            LambdaQueryWrapper<WmsInboundOrderDetailPO> queryWrapper =new LambdaQueryWrapper<WmsInboundOrderDetailPO>();
            queryWrapper.select(WmsInboundOrderDetailPO::getId, WmsInboundOrderDetailPO::getJadeId);
            queryWrapper.eq(WmsInboundOrderDetailPO::getInboundOrderCode,wmsOutboundOrderDO.getInboundOrderCode());

            List<WmsInboundOrderDetailPO> list = iWmsInboundOrderDetailService.list(queryWrapper);

            //生成对应的Map<jadeId,inboundDetailID> 然后给到下面的设置明细信息里面去
            jadeIdMap =
                    list.stream().collect(Collectors.toMap(WmsInboundOrderDetailPO::getJadeId, WmsInboundOrderDetailPO::getId));

        } else {
            jadeIdMap = null;
            isReturnOutbound = false;
        }

        //获取翡翠出库的状态
        String jadeStatus = OutboundType.getByCode(wmsOutboundOrderDO.getOrderType()).getJadeStatusCode();
        String currentOrderCode = wmsOutboundOrderDO.getOrderCode();
        boolean orderCodeExist= wmsOutboundOrderService.checkOrderCodeExist(currentOrderCode);

        // 借货出库，判断是否需要审核——60日内不用审核
        boolean needAudit = isBorrowOutBound&&wmsOutboundOrderDO.getLendDays()>60;
        Long auditId;

        // TODO: 接手思考：为什么要检查是否存在对应的单号？
        //  为什么如果存在对应单号是直接跳过创建出库单的操作，继续创建出库明细单，而不是出错？
        long id = 0L;
        if (!orderCodeExist){
            id = idGenerator.nextId("outbound");
            //不存在出库单 先新建出库单，拿到出库单编码后进行生成出库单明细
            wmsOutboundOrderDO.setId(id);
            wmsOutboundOrderDO.setTenantId(getLoginUser().getTenantId());
            wmsOutboundOrderDO.setDelFlag("0");
            wmsOutboundOrderDO.setCreateBy(getLoginUser().getUserId().toString());
            wmsOutboundOrderDO.setDeptId(DeptConstants.DEPT_ID_DEFAULT);
            wmsOutboundOrderDO.setCreateTime(new Date());
            wmsOutboundOrderDO.setUpdateBy(getLoginUser().getUserId().toString());
            wmsOutboundOrderDO.setUpdateTime(new Date());
            if (DataUtils.isNotEmpty(byInboundOrderCode)){
                // 入库单不为空
                wmsOutboundOrderDO.setInboundOrderId(byInboundOrderCode.getId());
            }
            // 创建借货出库审核单
            if (needAudit) {
                if ((auditId = auditOrderService.create("1", 1L))==null) {
                    return error("创建审核单失败!");
                }
            }
            else auditId = null;
            wmsOutboundOrderDO.setAuditId(auditId);
            // 出库单写入数据库
            wmsOutboundOrderService.save(wmsOutboundOrderDO);
        }
        else logger.warn("已存在出库单: {}",currentOrderCode);


        // 设置明细信息以及更新翡翠状态
        List<WmsOutboundOrderDetailDO> wmsOutboundOrderDetailDOList = new ArrayList<>();
        wmsJadeList.forEach(wmsJade ->{
            WmsOutboundOrderDetailDO wmsOutboundOrderDetailDO = new WmsOutboundOrderDetailDO();
            wmsOutboundOrderDetailDO.setOutboundOrderCode(currentOrderCode);
            wmsOutboundOrderDetailDO.setDelFlag("0");
            wmsOutboundOrderDetailDO.setJadeId(wmsJade.getId());
            wmsOutboundOrderDetailDO.setJadeCode(wmsJade.getJadeCode());
            wmsOutboundOrderDetailDO.setJadeBarcode(wmsJade.getJadeBarcode());
            wmsOutboundOrderDetailDO.setOutboundCount(wmsJade.getJadeQuantity());
            //是还货出库
            if (isReturnOutbound && DataUtils.isNotEmpty(jadeIdMap)){
                wmsOutboundOrderDetailDO.setInboundOrderDetailId(jadeIdMap.get(wmsJade.getId()));
            }
            // 不需要审核
            if(!needAudit){
                //更新仓位库存
                updateStorageInventory(wmsJade);
                //更新翡翠的出库状态
                iWmsJadeService.updateJadeStatus(jadeStatus,wmsJade.getId());
            }

            wmsOutboundOrderDetailDOList.add(wmsOutboundOrderDetailDO);
        });

        // 明细写入数据库
        outboundOrderDetailService.saveBatch(wmsOutboundOrderDetailDOList);

        return AjaxResult.success();
    }



    private void updateStorageInventory(WmsJade wmsJade){
        wmsWarehouseStorageService.updateInventory(wmsJade.getStorageId(),wmsJade.getJadeQuantity());
    }


    /**
     * 获取出库单编码
     *
     * @return
     * @throws InterruptedException
     */
    @PreAuthorize("@ss.hasPermi('system:outboundOrder:add')")
    @GetMapping("/getOutboundOrderCode")
    public String getOutboundOrderCode() throws InterruptedException {
        // 返回租户编码
        return wmsOutboundOrderService.getNewCode(SecurityUtils.getLoginUser().getUser());
    }


    /**
     * 合并出库单
     */
    @PreAuthorize("@ss.hasPermi('system:outboundOrder:add')")
    @Log(title = "合并出库单", businessType = BusinessType.INSERT)
    @PostMapping("/merge")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult merge(@RequestBody List<Long> outboundOrderIds) throws InterruptedException {
        wmsOutboundOrderService.merge(outboundOrderIds);
        return success();
    }
}
